using System;
using System.Collections.Generic;
using System.Text;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Collections;

namespace Decav.Collections.ObjectModel
{

    /// <summary>
    /// A read-only wrapper around a dictionary that can be exposed in object models without the risk of outsiders modifying
    /// your collection.
    /// </summary>
    /// <remarks>
    /// This class is simply a wrapper around a WatchableDictionary and is not a copy. Therefore changes to the underlying dictionary
    /// will be reflected in this instance as well.
    /// </remarks>
    /// <typeparam name="K">The type of key.</typeparam>
    /// <typeparam name="V">The type of value.</typeparam>
    [Serializable]
    public class WatchableReadOnlyDictionary<K, V> : IDictionary<K, V>
    {

        #region Field Declarations

        /// <summary>
        /// The inner dictionary that contains the keys and values in this dictionary.
        /// </summary>
        protected readonly WatchableDictionary<K, V> InnerDictionary;

        #endregion

        #region Event Declarations

        /// <summary>
        /// Event that is raised when an item is added to the collection.
        /// </summary>
        [Description( "Event that is raised when an item is added to the collection." )]
        public event ItemAddedEventHandler<KeyValuePair<K, V>> Added;

        /// <summary>
        /// Event that is raised when an item is removed from the collection.
        /// </summary>
        [Description( "Event that is raised when an item is removed from the collection." )]
        public event ItemRemovedEventHandler<KeyValuePair<K, V>> Removed;

        /// <summary>
        /// Event that is raised when an existing item in the list is modified.
        /// </summary>
        [Description( "Event that is raised when an existing item in the list is modified." )]
        public event ItemModifiedEventHandler<KeyValuePair<K, V>> Modified;

        /// <summary>
        /// Event that is raised when the collection is cleared of all items.
        /// </summary>
        [Description( "Event that is raised when the collection is cleared of all items." )]
        public event EventHandler Cleared;

        #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="T:WatchableReadOnlyDictionary`2"/> class.
        /// </summary>
        /// <param name="innerDictionary">The inner dictionary.</param>
        public WatchableReadOnlyDictionary( WatchableDictionary<K, V> innerDictionary )
        {

            if ( innerDictionary == null )
                throw new ArgumentNullException( "innerDictionary" );

            // Store the inner dictionary
            InnerDictionary = innerDictionary;

            // Bind to events
            InnerDictionary.Added += new ItemAddedEventHandler<KeyValuePair<K, V>>( InnerDictionary_Added );
            InnerDictionary.Removed += new ItemRemovedEventHandler<KeyValuePair<K, V>>( InnerDictionary_Removed );
            InnerDictionary.Modified += new ItemModifiedEventHandler<KeyValuePair<K, V>>( InnerDictionary_Modified );
            InnerDictionary.Cleared += new EventHandler( InnerDictionary_Cleared );

        }

        #endregion

        #region InnerDictionary Event Handlers

        /// <summary>
        /// Handles the Added event of the InnerDictionary object.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The ItemAddedEventArgs instance containing the event data.</param>
        private void InnerDictionary_Added( object sender, ItemAddedEventArgs<KeyValuePair<K, V>> e )
        {
            OnAdded( e );
        }

        /// <summary>
        /// Handles the Cleared event of the InnerDictionary object.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void InnerDictionary_Cleared( object sender, EventArgs e )
        {
            OnCleared( e );
        }

        /// <summary>
        /// Handles the Modified event of the InnerDictionary object.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The ItemModifiedEventArgs instance containing the event data.</param>
        private void InnerDictionary_Modified( object sender, ItemModifiedEventArgs<KeyValuePair<K, V>> e )
        {
            OnModified( e );
        }

        /// <summary>
        /// Handles the Removed event of the InnerDictionary object.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The ItemRemovedEventArgs instance containing the event data.</param>
        private void InnerDictionary_Removed( object sender, ItemRemovedEventArgs<KeyValuePair<K, V>> e )
        {
            OnRemoved( e );
        }

        #endregion

        #region Event Invoke Methods

        /// <summary>
        /// Raises the Added event to event subscribers.
        /// </summary>
        /// <remarks>
        /// Note to inheritors: You must call the base method in order to ensure that the event is raised and clean up
        /// work is properly performed.
        /// </remarks>
        /// <param name="e">Event arguments.</param>
        protected virtual void OnAdded( ItemAddedEventArgs<KeyValuePair<K, V>> e )
        {

            // Invoke the event handlers
            if ( Added != null )
            {
                Added( this, e );
            }   // if

        }

        /// <summary>
        /// Raises the Removed event to event subscribers.
        /// </summary>
        /// <remarks>
        /// Note to inheritors: You must call the base method in order to ensure that the event is raised and clean up
        /// work is properly performed.
        /// </remarks>
        /// <param name="e">Event arguments.</param>
        protected virtual void OnRemoved( ItemRemovedEventArgs<KeyValuePair<K, V>> e )
        {

            // Invoke the event handlers
            if ( Removed != null )
            {
                Removed( this, e );
            }   // if

        }

        /// <summary>
        /// Raises the Modified event to event subscribers.
        /// </summary>
        /// <remarks>
        /// Note to inheritors: You must call the base method in order to ensure that the event is raised and clean up
        /// work is properly performed.
        /// </remarks>
        /// <param name="e">Event arguments.</param>
        protected virtual void OnModified( ItemModifiedEventArgs<KeyValuePair<K, V>> e )
        {

            // Invoke the event handlers
            if ( Modified != null )
            {
                Modified( this, e );
            }   // if

        }

        /// <summary>
        /// Raises the Cleared event to event subscribers.
        /// </summary>
        /// <remarks>
        /// Note to inheritors: You must call the base method in order to ensure that the event is raised and clean up
        /// work is properly performed.
        /// </remarks>
        /// <param name="e">Event arguments.</param>
        protected virtual void OnCleared( EventArgs e )
        {

            // Invoke the event handlers
            if ( Cleared != null )
            {
                Cleared( this, e );
            }   // if

        }

        #endregion

        #region Collection Members

        /// <summary>
        /// Gets the number of elements contained in the dictionary.
        /// </summary>
        /// <returns>The number of elements contained in the dictionary.</returns>
        public int Count
        {
            get
            {
                return InnerDictionary.Count;
            }
        }

        /// <summary>
        /// Obtains the value in the dictionary with the specified <paramref name="key"/>.
        /// </summary>
        /// <param name="key">The key of the item to retrieve.</param>
        /// <returns>The item in the collection with the specified <paramref name="key"/>.</returns>
        public V this[K key]
        {
            get
            {
                return InnerDictionary[key];
            }
        }

        /// <summary>
        /// Determines whether the <see cref="T:System.Collections.Generic.IDictionary`2"></see> contains an element with the specified key.
        /// </summary>
        /// <param name="key">The key to locate in the <see cref="T:System.Collections.Generic.IDictionary`2"></see>.</param>
        /// <returns>
        /// true if the <see cref="T:System.Collections.Generic.IDictionary`2"></see> contains an element with the key; otherwise, false.
        /// </returns>
        /// <exception cref="T:System.ArgumentNullException">key is null.</exception>
        public bool ContainsKey( K key )
        {
            return InnerDictionary.ContainsKey( key );
        }

        /// <summary>
        /// Gets an <see cref="T:System.Collections.Generic.ICollection`1"></see> containing the keys of the <see cref="T:System.Collections.Generic.IDictionary`2"></see>.
        /// </summary>
        /// <value></value>
        /// <returns>An <see cref="T:System.Collections.Generic.ICollection`1"></see> containing the keys of the object that implements <see cref="T:System.Collections.Generic.IDictionary`2"></see>.</returns>
        public ICollection<K> Keys
        {
            get
            {
                return InnerDictionary.Keys;
            }
        }

        /// <summary>
        /// Gets an <see cref="T:System.Collections.Generic.ICollection`1"></see> containing the values in the <see cref="T:System.Collections.Generic.IDictionary`2"></see>.
        /// </summary>
        /// <value></value>
        /// <returns>An <see cref="T:System.Collections.Generic.ICollection`1"></see> containing the values in the object that implements <see cref="T:System.Collections.Generic.IDictionary`2"></see>.</returns>
        public ICollection<V> Values
        {
            get
            {
                return InnerDictionary.Values;
            }
        }

        /// <summary>
        /// Gets the value associated with the specified key.
        /// </summary>
        /// <param name="key">The key whose value to get.</param>
        /// <param name="value">When this method returns, the value associated with the specified key, if the key is found; otherwise, the default value for the 
        /// type of the value parameter. This parameter is passed uninitialized.</param>
        /// <returns>true if the object that implements IDictionary contains an element with the specified key; otherwise, false.</returns>
        public bool TryGetValue( K key, out V value )
        {
            return InnerDictionary.TryGetValue( key, out value );
        }

        #endregion

        #region IDictionary<K,V> Members

        /// <summary>
        /// Adds an element with the provided key and value to the <see cref="T:System.Collections.Generic.IDictionary`2"></see>.
        /// </summary>
        /// <param name="key">The object to use as the key of the element to add.</param>
        /// <param name="value">The object to use as the value of the element to add.</param>
        /// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.IDictionary`2"></see> is read-only.</exception>
        /// <exception cref="T:System.ArgumentException">An element with the same key already exists in the <see cref="T:System.Collections.Generic.IDictionary`2"></see>.</exception>
        /// <exception cref="T:System.ArgumentNullException">key is null.</exception>
        void IDictionary<K, V>.Add( K key, V value )
        {
            throw new NotSupportedException( "The collection is read-only." );
        }

        /// <summary>
        /// Removes the element with the specified key from the <see cref="T:System.Collections.Generic.IDictionary`2"></see>.
        /// </summary>
        /// <param name="key">The key of the element to remove.</param>
        /// <returns>
        /// true if the element is successfully removed; otherwise, false.  This method also returns false if key was not found in the original <see cref="T:System.Collections.Generic.IDictionary`2"></see>.
        /// </returns>
        /// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.IDictionary`2"></see> is read-only.</exception>
        /// <exception cref="T:System.ArgumentNullException">key is null.</exception>
        bool IDictionary<K, V>.Remove( K key )
        {
            throw new NotSupportedException( "The collection is read-only." );
        }

        /// <summary>
        /// Gets or sets the element with the specified key.
        /// </summary>
        /// <param name="key">The key of the element to get or set.</param>
        /// <returns>The element with the specified key.</returns>
        V IDictionary<K, V>.this[K key]
        {
            get
            {
                return InnerDictionary[key];
            }
            set
            {
                throw new NotSupportedException( "The collection is read-only." );
            }
        }

        #endregion

        #region ICollection<KeyValuePair<K,V>> Members

        /// <summary>
        /// Adds an item to the <see cref="T:System.Collections.Generic.ICollection`1"></see>.
        /// </summary>
        /// <param name="item">The object to add to the <see cref="T:System.Collections.Generic.ICollection`1"></see>.</param>
        /// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"></see> is read-only.</exception>
        void ICollection<KeyValuePair<K, V>>.Add( KeyValuePair<K, V> item )
        {
            throw new NotSupportedException( "The collection is read-only." );
        }

        /// <summary>
        /// Removes all items from the <see cref="T:System.Collections.Generic.ICollection`1"></see>.
        /// </summary>
        /// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"></see> is read-only. </exception>
        void ICollection<KeyValuePair<K, V>>.Clear( )
        {
            throw new NotSupportedException( "The collection is read-only." );
        }

        /// <summary>
        /// Determines whether the <see cref="T:System.Collections.Generic.ICollection`1"></see> contains a specific value.
        /// </summary>
        /// <param name="item">The object to locate in the <see cref="T:System.Collections.Generic.ICollection`1"></see>.</param>
        /// <returns>
        /// true if item is found in the <see cref="T:System.Collections.Generic.ICollection`1"></see>; otherwise, false.
        /// </returns>
        bool ICollection<KeyValuePair<K, V>>.Contains( KeyValuePair<K, V> item )
        {
            return InnerDictionary.ContainsKey( item.Key );
        }

        /// <summary>
        /// Copies the elements of the <see cref="T:System.Collections.Generic.ICollection`1"></see> to an <see cref="T:System.Array"></see>, starting at a particular <see cref="T:System.Array"></see> index.
        /// </summary>
        /// <param name="array">The one-dimensional <see cref="T:System.Array"></see> that is the destination of the elements copied from <see cref="T:System.Collections.Generic.ICollection`1"></see>. The <see cref="T:System.Array"></see> must have zero-based indexing.</param>
        /// <param name="arrayIndex">The zero-based index in array at which copying begins.</param>
        /// <exception cref="T:System.ArgumentOutOfRangeException">arrayIndex is less than 0.</exception>
        /// <exception cref="T:System.ArgumentNullException">array is null.</exception>
        /// <exception cref="T:System.ArgumentException">array is multidimensional.-or-arrayIndex is equal to or greater than the length of array.-or-The number of elements in the source <see cref="T:System.Collections.Generic.ICollection`1"></see> is greater than the available space from arrayIndex to the end of the destination array.-or-Type T cannot be cast automatically to the type of the destination array.</exception>
        void ICollection<KeyValuePair<K, V>>.CopyTo( KeyValuePair<K, V>[] array, int arrayIndex )
        {
            ( (ICollection<KeyValuePair<K, V>>)InnerDictionary ).CopyTo( array, arrayIndex );
        }

        /// <summary>
        /// Gets a value indicating whether the <see cref="T:System.Collections.Generic.ICollection`1"></see> is read-only.
        /// </summary>
        /// <value></value>
        /// <returns>true if the <see cref="T:System.Collections.Generic.ICollection`1"></see> is read-only; otherwise, false.</returns>
        bool ICollection<KeyValuePair<K, V>>.IsReadOnly
        {
            get
            {
                return true;
            }
        }

        /// <summary>
        /// Removes the first occurrence of a specific object from the <see cref="T:System.Collections.Generic.ICollection`1"></see>.
        /// </summary>
        /// <param name="item">The object to remove from the <see cref="T:System.Collections.Generic.ICollection`1"></see>.</param>
        /// <returns>
        /// true if item was successfully removed from the <see cref="T:System.Collections.Generic.ICollection`1"></see>; otherwise, false. This method also returns false if item is not found in the original <see cref="T:System.Collections.Generic.ICollection`1"></see>.
        /// </returns>
        /// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"></see> is read-only.</exception>
        bool ICollection<KeyValuePair<K, V>>.Remove( KeyValuePair<K, V> item )
        {
            throw new NotSupportedException( "The collection is read-only." );
        }

        #endregion

        #region IEnumerable<KeyValuePair<K,V>> Members

        /// <summary>
        /// Returns an enumerator that iterates through the collection.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.Collections.Generic.IEnumerator`1"></see> that can be used to iterate through the collection.
        /// </returns>
        IEnumerator<KeyValuePair<K, V>> IEnumerable<KeyValuePair<K, V>>.GetEnumerator( )
        {
            return ( (IEnumerable<KeyValuePair<K, V>>)InnerDictionary ).GetEnumerator( );
        }

        #endregion

        #region IEnumerable Members

        /// <summary>
        /// Returns an enumerator that iterates through a collection.
        /// </summary>
        /// <returns>
        /// An <see cref="T:System.Collections.IEnumerator"></see> object that can be used to iterate through the collection.
        /// </returns>
        IEnumerator IEnumerable.GetEnumerator( )
        {
            return ( (IEnumerable)InnerDictionary ).GetEnumerator( );
        }

        #endregion

    }   // class

}   // namespace
